package com.romadan.utils.math;

public class Complex extends Object {
	public static final Complex ZERO = new Complex(0, 0);
	public static final Complex ONE = new Complex(1, 0);
	public static final Complex TWO = new Complex(2, 0);
	public static final Complex FOUR = new Complex(4, 0);

	private double x, y;

	/**
	 * Constructs the complex number z = u + i*v
	 * 
	 * @param u
	 *            Real part
	 * @param v
	 *            Imaginary part
	 */
	public Complex(double u, double v) {
		x = u;
		y = v;
	}

	/**
	 * Real part of this Complex number (the x-coordinate in rectangular
	 * coordinates).
	 * 
	 * @return Re[z] where z is this Complex number.
	 */
	public double real() {
		return x;
	}

	/**
	 * Imaginary part of this Complex number (the y-coordinate in rectangular
	 * coordinates).
	 * 
	 * @return Im[z] where z is this Complex number.
	 */
	public double imag() {
		return y;
	}

	/**
	 * Modulus of this Complex number (the distance from the origin in polar
	 * coordinates).
	 * 
	 * @return |z| where z is this Complex number.
	 */
	public double mod() {
		if (x != 0 || y != 0) {
			return Math.sqrt(x * x + y * y);
		} else {
			return 0d;
		}
	}

	/**
	 * Argument of this Complex number (the angle in radians with the x-axis in
	 * polar coordinates).
	 * 
	 * @return arg(z) where z is this Complex number.
	 */
	public double arg() {
		return Math.atan2(y, x);
	}

	/**
	 * Complex conjugate of this Complex number (the conjugate of x+i*y is
	 * x-i*y).
	 * 
	 * @return z-bar where z is this Complex number.
	 */
	public Complex conj() {
		return new Complex(x, -y);
	}

	/**
	 * Addition of Complex numbers (doesn't change this Complex number). <br>
	 * (x+i*y) + (s+i*t) = (x+s)+i*(y+t).
	 * 
	 * @param w
	 *            is the number to add.
	 * @return z+w where z is this Complex number.
	 */
	public Complex plus(Complex w) {
		return new Complex(x + w.real(), y + w.imag());
	}

	/**
	 * Subtraction of Complex numbers (doesn't change this Complex number). <br>
	 * (x+i*y) - (s+i*t) = (x-s)+i*(y-t).
	 * 
	 * @param w
	 *            is the number to subtract.
	 * @return z-w where z is this Complex number.
	 */
	public Complex minus(Complex w) {
		return new Complex(x - w.real(), y - w.imag());
	}

	public Complex minus(double w) {
		return new Complex(x - w, y);
	}

	/**
	 * Complex multiplication (doesn't change this Complex number).
	 * 
	 * @param w
	 *            is the number to multiply by.
	 * @return z*w where z is this Complex number.
	 */
	public Complex mul(Complex w) {
		return new Complex(x * w.real() - y * w.imag(), x * w.imag() + y
				* w.real());
	}

	public Complex mul(double w) {
		return new Complex(x * w, y * w);
	}

	/**
	 * Division of Complex numbers (doesn't change this Complex number). <br>
	 * (x+i*y)/(s+i*t) = ((x*s+y*t) + i*(y*s-y*t)) / (s^2+t^2)
	 * 
	 * @param w
	 *            is the number to divide by
	 * @return new Complex number z/w where z is this Complex number
	 */
	public Complex div(Complex w) {
		double den = Math.pow(w.mod(), 2);
		return new Complex((x * w.real() + y * w.imag()) / den,
				(y * w.real() - x * w.imag()) / den);
	}

	/**
	 * Complex exponential (doesn't change this Complex number).
	 * 
	 * @return exp(z) where z is this Complex number.
	 */
	public Complex exp() {
		return new Complex(Math.exp(x) * Math.cos(y), Math.exp(x) * Math.sin(y));
	}

	/**
	 * Complex square root (doesn't change this complex number). Computes the
	 * principal branch of the square root, which is the value with 0 <= arg <
	 * pi.
	 * 
	 * @return sqrt(z) where z is this Complex number.
	 */
	public Complex sqrt() {
		double r = Math.sqrt(this.mod());
		double theta = this.arg() / 2;
		return new Complex(r * Math.cos(theta), r * Math.sin(theta));
	}
	
	public Complex pow(int n) {
		if (n == 2) {
			return this.mul(this);
		} else {
			return null;
		}
	}

	/**
	 * String representation of this Complex number.
	 * 
	 * @return x+i*y, x-i*y, x, or i*y as appropriate.
	 */
	public String toString() {
		if (x != 0 && y > 0) {
			return x + " + " + y + "i";
		}
		if (x != 0 && y < 0) {
			return x + " - " + (-y) + "i";
		}
		if (y == 0) {
			return String.valueOf(x);
		}
		if (x == 0) {
			return y + "i";
		}
		// shouldn't get here (unless Inf or NaN)
		return x + " + i*" + y;

	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		long temp;
		temp = Double.doubleToLongBits(x);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(y);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Complex other = (Complex) obj;
		if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
			return false;
		if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
			return false;
		return true;
	}
}
